package network

import (
	"context"
	"github.com/ezcloud/ezapp/pkg/core/log"
	"golang.org/x/sys/unix"
	"net"
	"syscall"
	"time"
)

type socktFunc = func(network, address string, c syscall.RawConn) error

func controlFunc(reuseport bool) socktFunc {
	return func(network, address string, c syscall.RawConn) error {
		if reuseport {
			c.Control(func(fd uintptr) {
				var f = int(fd)
				var val = 1
				err := syscall.SetsockoptInt(f, syscall.SOL_SOCKET, unix.SO_REUSEPORT, val)
				if err != nil {
					log.Errorf("setsockopt [%d] SO_REUSEPORT: %s", f, err.Error())
				}
			})
		}
		return nil
	}
}

func Listen(network, address string /*, reusePort bool*/, keepaliveTime time.Duration) (net.Listener, error) {
	var lc net.ListenConfig

	// 2025-10-04
	// ${golang}/src/net/sockopt_linux.go 中已经初始就设为了 SO_REUSEADDR
	// setDefaultListenerSockopts 已经设置 SO_REUSEADDR
	// 此块代码也不在需要了.
	// lc.Control = controlFunc(reusePort)

	ln, err := lc.Listen(context.Background(), network, address)
	if err != nil {
		return nil, err
	}

	if tcpln, ok := ln.(*net.TCPListener); ok {
		return &tcpKeepaliveListener{TCPListener: tcpln, keepalivePeriod: keepaliveTime}, nil
	}

	return ln, nil
}

type tcpKeepaliveListener struct {
	*net.TCPListener
	keepalivePeriod time.Duration
}

func (ln *tcpKeepaliveListener) Accept() (net.Conn, error) {
	tc, err := ln.TCPListener.AcceptTCP()
	if err != nil {
		return nil, err
	}

	if ln.keepalivePeriod > 0 {
		// golang 实现
		_ = tc.SetKeepAliveConfig(net.KeepAliveConfig{
			Enable:   true,
			Idle:     ln.keepalivePeriod,
			Count:    3,
			Interval: ln.keepalivePeriod / 3,
		})

		// hack
		// hackKeepAlivePeriod(tc, int(ln.keepalivePeriod/time.Second))

		return tc, nil

	}
	return tc, nil
}

func hackKeepAlivePeriod(tc *net.TCPConn, interval int) {
	c, _ := tc.SyscallConn()

	_ = c.Control(func(fd uintptr) {
		var f int = int(fd)
		var val = 1
		err := syscall.SetsockoptInt(f, syscall.SOL_SOCKET, unix.SO_KEEPALIVE, val)
		if err != nil {
			log.Errorf("setsockopt [%d] SO_KEEPALIVE: %s", f, err.Error())
			return
		}

		val = interval
		err = syscall.SetsockoptInt(f, syscall.IPPROTO_TCP, unix.TCP_KEEPIDLE, val)
		if err != nil {
			log.Errorf("setsockopt [%d] TCP_KEEPIDLE: %s", f, err.Error())
			return
		}

		val = interval / 3
		err = syscall.SetsockoptInt(f, syscall.IPPROTO_TCP, unix.TCP_KEEPINTVL, val)
		if err != nil {
			log.Errorf("setsockopt [%d] TCP_KEEPINTVL: %s", f, err.Error())
			return
		}

		val = 3
		err = syscall.SetsockoptInt(f, syscall.IPPROTO_TCP, unix.TCP_KEEPCNT, val)
		if err != nil {
			log.Errorf("setsockopt [%d] TCP_KEEPCNT: %s", f, err.Error())
			return
		}
	})
	/* redis server 源码!
	   	int val = 1;
	   	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1) {
	   		printf("setsockopt SO_KEEPALIVE: %s", strerror(errno));
	   		return -1;
	   	}

	   // Send first probe after `interval' seconds.
	   	val = interval;

	   	if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
	   		printf("setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
	   		return -1;
	   	}

	   // Send next probes after the specified interval. Note that we set the
	   // delay as interval / 3, as we send three probes before detecting
	   // an error (see the next setsockopt call).
	   	val = interval/3;

	   	if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
	   		printf("setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
	   		return -1;
	   	}

	   // Consider the socket in error state after three we send three ACK
	   // probes without getting a reply.
	   	val = 3;

	   	if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
	   		printf("setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
	   		return -1;
	   	}
	*/
}
